Dx.declare('Dx.system.Detector', {

   '@action': {

      'register': [

         'validate',

         'process'

      ],

      'is': [

         'validate',

         'process'

      ]

   },

   cache: undefined,

   detectors: undefined,

   rootNs: 'Dx.system.detection',

   defaultDetector: 'default',

   queryRe: /^([^\:]*):(.+)$/,

   constructor: function() {

      this.cache = {};

      this.detectors = {};

      this.$super(arguments);

   },

   register_validate: function(o, className) {

      var D = Dx;

      o.stop = !D.isString(className, true) || !D.Class.isDeclared(className);

   },

   register_process: function(o, className) {

      var root = this.rootNs;

      var cache = this.cache;

      var tag = className;

      if (className.substring(0, root.length + 1) == root + '.') {

         tag = className.substring(root.length + 1, className.length);

      }

      // register!
      this.detectors[':'+tag] = Dx.instantiate(className);

   },

   is_validate: function(o, query, target) {

      var D = Dx;

      var detectors = this.detectors;

      var m = D.isString(query, true) && query.match(this.queryRe);

      o.returnValue = false;

      o.stop = true;

      if (m) {

         tag = ':' + (m[1] || this.defaultDetector);

         if (tag in detectors) {

            o.params[1] = m[2];

            o.detectorName = tag;

            o.detector = detectors[tag];

            o.hasTarget = arguments.length == 3;

            o.stop = false;

         }

      }

   },

   is_process: function(o, query, target) {

      var cache = this.cache;

      var cacheTag = o.detectorName + ':' + query;

      var hasTarget = o.hasTarget;

      var result;

      if (!hasTarget && cacheTag in cache) {

         result = cache[cacheTag];

      } else {

         result = o.detector.is(query, target);

         // cache result if no target
         if (hasTarget) {

            cache[cacheTag] = result;

         }

      }

      o.returnValue = result;

   }


});


/**
 * Find boolean value of global detection results
 * @param {String} detectName the named detection property
 * @return {Boolean} true if detectName exists and has true value in it.
 */
Dx.is = function(detectName) {

   var D = Dx;

   var detected = D.detected;

   var root = 'Dx.system.detection';

   var index = D.isString(detectName, true) ? detectName.indexOf(':') : -1;

   var name = detectName;

   var ns = '';

   var detector;

   if (index != -1) {

      ns = detectName.substring(0, index);

      name = detectName.substring(index+1, detectName.length);

   }

   detector = ns && D.getInstance(root + '.' + ns);

   return detector ?

      detector.is(name) : (

         detected.hasOwnProperty(name) && detected[name]
      );

};
